﻿
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif
namespace GameBoxTools
{
	//分析类型
    public enum StatisticType
    {
        None,
        Overdraw,
        TransparentRate,
    }
	
    public class BaseStatistic : MonoBehaviour
    {
        internal class CameraRenderTextrue
        {
            private RenderTexture renderTexture;//相机渲染的目标纹理
            public Camera rtCamera;
            public GameObject cameraGo;

            public CameraRenderTextrue(GameObject cameraPrefab, int index, int cullingmask)
            {
                cameraGo = Instantiate(cameraPrefab);
                cameraGo.name = string.Format("rendercamera{0}", index);
                rtCamera = cameraGo.GetComponent<Camera>();
                int cm = cullingmask;
                rtCamera.cullingMask = 1 << cm;

                //渲染目标纹理
                renderTexture = RenderTexture.GetTemporary(1024, 728, 24);//new RenderTexture(1024, 728, 24);
                renderTexture.name = "StatisticRT";
                renderTexture.antiAliasing = 1;

                rtCamera.targetTexture = renderTexture;
            }

            public void Destroy()
            {
                if (cameraGo != null)
                {
                    DestroyImmediate(cameraGo);
                    cameraGo = null;
                }
                if (renderTexture != null)
                {
                    RenderTexture.ReleaseTemporary(renderTexture);
                    //DestroyImmediate(renderTexture);
                    renderTexture = null;
                }
            }
        }

        //粒子包围盒
        internal struct PSBounds
        {
            private Vector3 center;
            private Vector3 size;
            public Bounds bounds;

            public PSBounds(Vector3 min, Vector3 max)
            {
                //包围盒8个顶点，逆时针排序
                //      2---3         6---7
                //正面  |   |    反面  |   |
                //      1---4         5---8
                //this.p1 = new Vector3(min.x, min.y, min.z);
                //this.p2 = new Vector3(min.x, max.y, min.z);
                //this.p3 = new Vector3(max.x, max.y, min.z);
                //this.p4 = new Vector3(max.x, min.y, min.z);

                //this.p5 = new Vector3(min.x, min.y, max.z);
                //this.p6 = new Vector3(min.x, max.y, max.z);
                //this.p7 = new Vector3(max.x, max.y, max.z);
                //this.p8 = new Vector3(max.x, min.y, max.z);

                this.center = new Vector3((max.x + min.x), (max.y + min.y), (max.z + min.z)) / 2;
                this.size = new Vector3((max.x - min.x), (max.y - min.y), (max.z - min.z));

                this.bounds = new Bounds(this.center, this.size);
            }

            public void UpdateBounds(Vector3 center_offset)
            {
                this.center += center_offset;
                this.bounds = new Bounds(this.center, size);
            }

            public void Clear()
            {
                this.center = Vector3.zero;
                this.size = Vector3.zero;
                this.bounds = default(Bounds);
            }
        }
		
		[HideInInspector]
        public string orignScene = string.Empty;//原场景

        protected const int rgbaCount = 4;
        protected Renderer[] psr;
        [HideInInspector]
        public bool[] wrong_material;
        private ParticleSystem[] ps;
        private Transform psGo;//粒子实例
        private float max_duration = 1f;
        private int frame_per_second = 30;//每秒30帧
        private int frame_sum = 30;//总帧数
        private float frame_delta_time = 0.33f;
        private int m_height = 0;
        private int m_width = 0;

        public GameObject cameraPrefab;
        private List<CameraRenderTextrue> crtList = new List<CameraRenderTextrue>();//相机个数
        private StatisticCreate sc;
        private BaseBatchStatistic bs;
        [Header("是否单个粒子分析")]
        public bool isSingle = true;//处理单个粒子
        protected virtual StatisticType Type
        {
            //处理类型：0.无效, 1.overdraw, 2:透明占比
            get { return StatisticType.None; }
        }

        private int layerIndex = 8;//用户layer起始索引

        private void Start()
        {
            if (this.isSingle)
            {
                this.Init();
                sc = GameObject.Find("LineGraph").GetComponent<StatisticCreate>();
            }
            else
                bs = GameObject.Find("AllPsProcess").GetComponent<BaseBatchStatistic>();
        }

        public void Init()
        {
            //粒子特效位置归0
            if (this.transform.childCount > 0)
            {
                psGo = this.transform.GetChild(0);
                if (null != psGo)
                {
					psGo.gameObject.SetActive(true);
                    psGo.localPosition = new Vector3(0, 0, 1.0f);//初始z值需要有些偏移，让相机可以看到
                }
            }

            ps = gameObject.GetComponentsInChildren<ParticleSystem>(true);
            for (int i = 0; i < ps.Length; ++i)
            {
                //获取最大持续时间
                if (this.max_duration < ps[i].main.duration)
                {
                    //有待商榷
                    this.max_duration = ps[i].main.duration;
                }
            }
            if (this.max_duration > 5.0f)
            {
                this.max_duration = 5.0f;
            }
            //this.max_duration = 1.0f;
            this.frame_sum = (int)(this.max_duration * this.frame_per_second);
            this.frame_delta_time = 1.0f / this.frame_per_second;

            foreach (var crt in crtList)
            {
                crt.Destroy();
            }
            crtList.Clear();

            psr = gameObject.GetComponentsInChildren<Renderer>(true);
            wrong_material = new bool[psr.Length];
            int cullingmask = 0;
            for (int i = 0; i < psr.Length; ++i)
            {
                //设置layer
                if (0 == i % rgbaCount)//每rendersCount个使用同一camera
                {
                    int cm = i / rgbaCount;
                    cullingmask = layerIndex + cm;

                    CameraRenderTextrue crt = new CameraRenderTextrue(cameraPrefab, cm, cullingmask);
                    crtList.Add(crt);
                }
                psr[i].gameObject.layer = cullingmask;
            }
            
            this.SetCameraView();
            this.SetMaterial();
        }
		
		public void SetCameraView()
        {
            this.SetSceneView(crtList[0].cameraGo.transform);//至少有一个相机
        }

        //将Scene场景的观察视图设为与相机一致
        private void SetSceneView(Transform camera)
        {
#if UNITY_EDITOR
            SceneView sv = SceneView.lastActiveSceneView;//只有Scene窗口可见时不为null
            if (null == sv)
            {
                var svs = SceneView.sceneViews;
                if (svs.Count > 0)
                {
                    sv = svs[0] as SceneView;
                }
            }

            if (null != sv)
            {
                sv.AlignViewToObject(camera);
                if (StatisticType.Overdraw == this.Type)
                {
                    sv.cameraMode = SceneView.GetBuiltinCameraMode(DrawCameraMode.Overdraw);//overdraw模式
                }
            }
#endif
        }

        protected virtual void SetMaterial()
        {
            
        }

        public void ShowOrHide(int index, bool isShow)
        {
			if(null != psr[index])
				psr[index].gameObject.SetActive(isShow);
        }

        public void ChooseObj(int index)
        {
#if UNITY_EDITOR
			if(null != psr[index])
			{
				EditorGUIUtility.PingObject(psr[index].gameObject);
				Selection.activeGameObject = psr[index].gameObject;
			}
            else
            {
                Debug.LogError("Render已丢失，请检查");
            }
#endif
        }

        public float GetCollectTime()
        {
            return this.max_duration;
        }

        /// <summary>
        /// 重新采集
        /// </summary>
        public void ReCollect()
        {
            this.isStartCollect = true;
            this.strat_frame = 0;

            if (null != psGo)
            {
                psGo.localPosition = new Vector3(0, 0, 1.0f);
            }
            this.isCalcBounds = true;
            this.min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
            this.max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
            this.psb.Clear();
            this.SimulateParticlesForDeltaTime(true);
			this.SetCameraView();

            System.GC.Collect();
        }
        
        int strat_frame = 0;
        bool isStartCollect = false;
        bool isCalcBounds = true;//计算包围盒阶段
        private void Update()
        {
            if (!isStartCollect) return;

            if(isCalcBounds)
            {
                this.CalcPsBoundsInfo();
            }
            else
            {
                this.CollectRTInfo();
            }
        }

        [Header("绘制粒子包围盒")]
        public bool drawPsBounds = true;
        private void OnDrawGizmos()
        {
            if (!drawPsBounds) return;
            
            Gizmos.color = Color.red;
            Gizmos.DrawWireCube(psb.bounds.center, psb.bounds.size);
        }

        private Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        private Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
        private PSBounds psb;
		private Vector3 psRootPos = new Vector3(1000, 1000, 0);
        /// <summary>
        /// 计算粒子包围盒，通过包围盒大小计算一个可以将粒子完全放置在视椎体中的z值
        /// </summary>
        void CalcPsBoundsInfo()
        {
            if (this.strat_frame >= this.frame_sum)
            {
                psb = new PSBounds(min, max);

                float y = psb.bounds.extents.y;
                float l_y = y / Mathf.Tan(crtList[0].rtCamera.fieldOfView * Mathf.Deg2Rad * 0.5f);

                float x = (psb.bounds.extents.x > psb.bounds.extents.z ? psb.bounds.extents.x : psb.bounds.extents.z);
                float h_fov = this.getHorizontalAngle(crtList[0].rtCamera);
                float l_x = x / Mathf.Tan(h_fov * Mathf.Deg2Rad * 0.5f);

                float z_dis = (l_x > l_y) ? l_x : l_y;

                psb.UpdateBounds(new Vector3(0, 0, z_dis));//包围盒中心跟随粒子特效位置调整
				Vector3 local_pos = psb.bounds.center - psRootPos;//PSRoot与渲染相机的世界坐标为(1000, 1000, 0)
                this.psGo.localPosition = local_pos;//粒子特效位置调整
                
                isCalcBounds = false;
                this.strat_frame = 0;

                this.SimulateParticlesForDeltaTime(true);
                //Debug.Log("min: " + min + "  max: " + max + "  bounds: " + psb.bounds);
            }
            else
            {
                this.SimulateParticlesForDeltaTime(false);
                this.getMin_Max();
                ++this.strat_frame;
            }
        }

        //获取包围盒最小、最大顶点
        void getMin_Max()
        {
            foreach (var item in psr)
            {
				if (null == item) continue;//有些脚本逻辑会删除Render
				
                if (item.bounds.size.x > 0 && item.bounds.size.y > 0 && item.bounds.size.z > 0)
                {
                    if (item.bounds.min.x < min.x) min.x = item.bounds.min.x;
                    if (item.bounds.min.y < min.y) min.y = item.bounds.min.y;
                    if (item.bounds.min.z < min.z) min.z = item.bounds.min.z;

                    if (item.bounds.max.x > max.x) max.x = item.bounds.max.x;
                    if (item.bounds.max.y > max.y) max.y = item.bounds.max.y;
                    if (item.bounds.max.z > max.z) max.z = item.bounds.max.z;
                }
            }
        }

        //获取横向Fov，camera.fieldOfView为竖直Fov
        private float getHorizontalAngle(Camera camera)
        {
            float vFOVrad = camera.fieldOfView * Mathf.Deg2Rad;
            float cameraHeightAt1 = Mathf.Tan(vFOVrad * .5f);
            return Mathf.Atan(cameraHeightAt1 * camera.aspect) * 2f * Mathf.Rad2Deg;
        }

        /// <summary>
        /// 采集纹理信息
        /// </summary>
        void CollectRTInfo()
        {
            if (this.strat_frame >= this.frame_sum)
            {
                this.isStartCollect = false;

                Util.rgbaCount = rgbaCount;
                //显示数据
                if (this.isSingle)
                    sc.Init(psr.Length, crtList.Count);
                else
                    bs.Init(psr.Length, crtList.Count);

                return;
            }
            else
            {
                this.SimulateParticlesForDeltaTime(false);
                //采集数据
                this.RenderToTex();
                ++this.strat_frame;
            }
        }

        void SimulateParticlesForDeltaTime(bool restart)
        {
            if (null == ps) return;
            
            if (ps.Length > 0)
            {
                foreach (ParticleSystem p in this.ps)
                {
                    p.Simulate(this.frame_delta_time, false, restart);
                }
            }
        }
		
		public void SimulateParticlesForDeltaTime(int frame)
        {
            if (null == ps) return;
            
            if (ps.Length > 0)
            {
                foreach (ParticleSystem p in this.ps)
                {
                    p.Simulate(frame * this.frame_delta_time, false, true);
                }
            }
        }

        private void OnDestroy()
        {
            this.Clear();

            mtree.Clear();
        }

        public void Clear()
        {
            foreach (var crt in this.crtList)
            {
                crt.Destroy();
            }
            this.crtList.Clear();
        }

        void RenderToTex()
        {
            List<Color32[]> texs = new List<Color32[]>();
            foreach (var crt in this.crtList)
            {
                RenderTexture rt = crt.rtCamera.targetTexture;
                //rtCamera.Render();
                RenderTexture.active = rt;

                if (0 == this.m_height)
                {
                    this.m_height = rt.height;
                }
                if (0 == this.m_width)
                {
                    this.m_width = rt.width;
                }

                Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
                tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
                tex.Apply();

                Color32[] pixels = tex.GetPixels32();
                texs.Add(pixels);

                DestroyImmediate(tex);
                tex = null;
            }

            this.ParseTex(texs);
        }

        MTree mtree = new MTree();
        /// <summary>
        /// 解析每帧数据
        /// </summary>
        /// <param name="texs">data</param>
        void ParseTex(List<Color32[]> texs)
        {
            mtree.Reset();
            var texCount = texs.Count;
            var pixelCount = texs[0].Length;

            uint[] rs = new uint[texCount];
            for (int i = 0; i < pixelCount; i++)
            {
                uint blackCount = 0;
                for (int j = 0; j < texCount; j++)
                {
                    Color32 color = texs[j][i];
                    if (color.a == 0 && color.r == 0 && color.g == 0 && color.b == 0)//注意：由于三角面有正反的区别，所以如果是反面的话取值也为Color(0, 0, 0, 0)
                    {
                        ++blackCount;
                        rs[j] = 0;
                        continue;
                    }
                    rs[j] = ((uint)color.r << 24) + ((uint)color.g << 16) + ((uint)color.b << 8) + (uint)color.a;
                }

                if (blackCount == texCount)
                    continue;

                var area = mtree.GetArea(rs);
                ++area.Count;
            }

            var list = mtree.GetAreaList();
            List<Area> list_clone = new List<Area>();
            list.ForEach(i => list_clone.Add(i));

            if (this.isSingle)
                sc.AddOriginData(list_clone);
            else
                bs.AddOriginData(list_clone);
        }
    }
}
